package com.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NioSelectorServer {

    /**
     * 综合阻塞与非阻塞,及无相关事件时程序阻塞
     * 基础nio，缺点为单次处理数据过大，程序阻塞时间过长，会影响其他连接处理
     */
    public static void main(String[] args) {
        new NioSelectorServer().selector();

    }

    public void selector() {
        try {
            // 创建socket
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.socket().bind(new InetSocketAddress(18801));
            serverSocketChannel.configureBlocking(false);

            // 使用selector(多路复用器)处理Channel，
            // epoll、select、poll
            Selector selector = Selector.open();

            // 将channel注册到selector
            // OP_ACCEPT接收连接继续事件，表示服务器监听到了客户连接，服务器可以接收这个连接了
            // OP_CONNECT:连接就绪事件，表示客户与服务器的连接已经建立成功
            // OP_READ:读就绪事件，表示通道中已经有了可读的数据，可以执行读操作了（通道目前有数据，可以进行读操作了）
            // OP_WRITE:写就绪事件，表示已经可以向通道写数据了（通道目前可以用于写操作）
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("启动完成,端口18801");

            while (true) {
                // 无相关事件则阻塞程序，感知注册到selector的channel事件（连接，数据传输等事件）
                selector.select();
                Set<SelectionKey> selectionKeySet = selector.selectedKeys();
                Iterator<SelectionKey> selectIter = selectionKeySet.iterator();

                while (selectIter.hasNext()) {
                    SelectionKey selectionKey = selectIter.next();
                    // 判断事件
                    if (selectionKey.isAcceptable()) {
                        // 连接事件,为连接注册需要selector监听的事件
                        ServerSocketChannel serverSocketChannel1 = (ServerSocketChannel) selectionKey.channel();
                        SocketChannel socketChannel = serverSocketChannel1.accept();

                        // 配置非阻塞
                        socketChannel.configureBlocking(false);

                        // 即使用selector管理channel事件，通过硬件感知事件以唤醒程序执行
                        // 为链接注册读事件，即接收客户端发来的数据
                        socketChannel.register(selector, SelectionKey.OP_READ);
                        System.out.println("建立连接");

                    } else if (selectionKey.isReadable()) {
                        // 读事件

                        // 获取注册对象
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(128);

                        // 清空缓冲区
                        buffer.clear();

                        // 连接中断此处会异常，暂未找到解决方法
                        int len = socketChannel.read(buffer);
                        if (len > 0) {
                            System.out.println("传入数据为" + new String (buffer.array()));
                        } else if (len == -1) {
                            System.out.println("连接已断开");
                            socketChannel.close();
                        }

                        // 数据回写
//                        ByteBuffer writeBuffer = ByteBuffer.allocate(128);
//                        writeBuffer.put("fdas".getBytes());
//                        socketChannel.write(writeBuffer);

                    }
                    // 移除处理过得key
                    selectIter.remove();
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}
